//! Transaction step implementation

use crate::error::{Error, Result};
use crate::models::{
    config::StepConfig,
    step::{IStep, StepType, StepResult, StepContext},
    Value, ValueMap,
};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use uuid::Uuid;

/// Transaction types
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TransactionType {
    /// Start a transaction
    Start,
    /// End a transaction
    End,
}

/// Transaction configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionConfig {
    /// Transaction name
    pub name: String,

    /// Transaction type (start/end)
    pub transaction_type: TransactionType,
}

/// Transaction step implementation
#[derive(Debug, Clone)]
pub struct TransactionStep {
    id: Uuid,
    config: StepConfig,
    transaction_config: TransactionConfig,
}

impl TransactionStep {
    /// Create a new Transaction step
    pub fn new(config: StepConfig, transaction_config: TransactionConfig) -> Self {
        Self {
            id: Uuid::new_v4(),
            config,
            transaction_config,
        }
    }

    /// Create a transaction start step
    pub fn start(transaction_name: String) -> Self {
        let config = StepConfig {
            step_name: format!("Transaction Start: {}", transaction_name),
            ..Default::default()
        };

        let transaction_config = TransactionConfig {
            name: transaction_name,
            transaction_type: TransactionType::Start,
        };

        Self::new(config, transaction_config)
    }

    /// Create a transaction end step
    pub fn end(transaction_name: String) -> Self {
        let config = StepConfig {
            step_name: format!("Transaction End: {}", transaction_name),
            ..Default::default()
        };

        let transaction_config = TransactionConfig {
            name: transaction_name,
            transaction_type: TransactionType::End,
        };

        Self::new(config, transaction_config)
    }
}

#[async_trait]
impl IStep for TransactionStep {
    fn name(&self) -> &str {
        &self.config.step_name
    }

    fn step_type(&self) -> StepType {
        StepType::Transaction
    }

    fn config(&self) -> &StepConfig {
        &self.config
    }

    fn config_mut(&mut self) -> &mut StepConfig {
        &mut self.config
    }

    async fn execute(&self, context: &mut StepContext) -> Result<StepResult> {
        let start_time = Instant::now();
        let mut step_result = StepResult::new(self.id, self.name().to_string(), self.step_type());

        let transaction_key = format!("transaction_{}", self.transaction_config.name);
        let current_timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64;

        match self.transaction_config.transaction_type {
            TransactionType::Start => {
                // Record transaction start time
                context.variables.insert(
                    transaction_key.clone(),
                    Value::Number(serde_json::Number::from(current_timestamp))
                );

                let mut response_data = ValueMap::new();
                response_data.insert("transaction_name".to_string(),
                    Value::String(self.transaction_config.name.clone()));
                response_data.insert("transaction_type".to_string(),
                    Value::String("start".to_string()));
                response_data.insert("start_time".to_string(),
                    Value::Number(serde_json::Number::from(current_timestamp)));

                step_result.data = Some(Value::Object(response_data.into_iter().collect()));
                step_result.success = true;
            }

            TransactionType::End => {
                // Check if transaction was started
                if let Some(start_time_value) = context.variables.get(&transaction_key) {
                    if let Some(start_time) = start_time_value.as_u64() {
                        let duration_ms = current_timestamp - start_time;

                        // Remove the transaction from context
                        context.variables.remove(&transaction_key);

                        let mut response_data = ValueMap::new();
                        response_data.insert("transaction_name".to_string(),
                            Value::String(self.transaction_config.name.clone()));
                        response_data.insert("transaction_type".to_string(),
                            Value::String("end".to_string()));
                        response_data.insert("start_time".to_string(),
                            Value::Number(serde_json::Number::from(start_time)));
                        response_data.insert("end_time".to_string(),
                            Value::Number(serde_json::Number::from(current_timestamp)));
                        response_data.insert("duration_ms".to_string(),
                            Value::Number(serde_json::Number::from(duration_ms)));
                        response_data.insert("duration_seconds".to_string(),
                            Value::Number(serde_json::Number::from_f64(duration_ms as f64 / 1000.0).unwrap()));

                        step_result.data = Some(Value::Object(response_data.into_iter().collect()));
                        step_result.success = true;

                        // Store transaction duration for reporting
                        let transaction_result_key = format!("transaction_result_{}", self.transaction_config.name);
                        context.variables.insert(
                            transaction_result_key,
                            Value::Number(serde_json::Number::from(duration_ms))
                        );
                    } else {
                        step_result.success = false;
                        step_result.error = Some(format!(
                            "Invalid start time for transaction '{}'",
                            self.transaction_config.name
                        ));
                    }
                } else {
                    step_result.success = false;
                    step_result.error = Some(format!(
                        "Transaction '{}' was not started or already ended",
                        self.transaction_config.name
                    ));
                }
            }
        }

        step_result.elapsed = start_time.elapsed().as_millis() as i64;
        Ok(step_result)
    }

    fn id(&self) -> Uuid {
        self.id
    }

    fn clone_step(&self) -> Box<dyn IStep> {
        Box::new(self.clone())
    }
}
